﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;

namespace MessageWorkflow.Handler
{
    /// <summary>
    /// Handles all messages of type T
    /// </summary>
    /// <typeparam name="T">The type of the message to handle</typeparam>
    public abstract class WorkflowHandler<T> : IWorkflowHandler
    {
        /// <summary>
        /// Constructs a new instance which doesnt handle derived types
        /// </summary>
        protected WorkflowHandler()
            : this(false)
        {
        }

        /// <summary>
        /// Constructs a new instance specifying wether this handler can handle types deriving from the type argument specified
        /// </summary>
        /// <param name="handleDerivedTypes">if true, types deriving from the generic type are handled</param>
        protected WorkflowHandler(bool handleDerivedTypes)
        {
            HandleDerivedTypes = handleDerivedTypes;
        }

        /// <summary>
        /// If true, types deriving from the generic type specified will be handeled as well
        /// </summary>
        public bool HandleDerivedTypes { get; protected set; }

        /// <summary>
        /// Determines wether the type specified can be handled by this handler
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public virtual bool CanHandle(Type type)
        {
            if (HandleDerivedTypes)
            {
                Type baseType = type;
                do
                {
                    if (baseType == typeof(T))
                    {
                        return true;
                    }
                    else
                    {
                        baseType = baseType.BaseType;
                    }
                }
                while (baseType != null);

                // if no basetype matches the type specified, return false
                return false;
            }
            else
            {
                return type == typeof(T);
            }
        }

        public virtual IAsyncResult BeginHandle(AsyncCallback callback, object message)
        {
            T typedMessage = (T)message;

            return BeginHandleMessage(callback, typedMessage);
        }

        public virtual void EndHandle(IAsyncResult result)
        {
            AsyncResult<T> typedResult = (AsyncResult<T>)result;

            EndHandleMessage(typedResult);

            if (typedResult != null)
            {
                typedResult.EndInvoke();
            }
        }

        protected abstract IAsyncResult BeginHandleMessage(AsyncCallback callback, T message);

        protected virtual void EndHandleMessage(IAsyncResult operation) { }
    }
}
